package com.gosling.cloudspace.tools;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.gosling.cloudspace.common.annotation.DictionaryCode;
import com.gosling.cloudspace.common.entity.FieldDictionary;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.beans.BeanGenerator;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

@Component
public class DicToolCopy {
    protected final Logger logger = LoggerFactory.getLogger(getClass());
    private static DicToolCopy util;
    private static Map<String, BeanCopier> beanCopierMap = new HashMap<>();
    /**
     * 集合对象class，对象对应需要显示字段String[]Class，BeanGenerator
     */
    private static Map<Class, Map<Class, BeanGenerator>> beanGeneraMap = new HashMap<>();
    /**
     * Map<对象, List<FieldDictionary>>
     */
    private static Map<Class, List<FieldDictionary>> dictionaryMap = new HashMap<>();
    private static Map<String, Map<String, String>> code2KeyValueMap = new LinkedHashMap<>(16, 0.75f, true);
//    @Autowired
//    private DictionaryMapper dictionaryMapper;

    /**
     * 若查询结果集中包含数据字典相关内容，需要进行数据字典键值对转换，可调用此方法。
     * 只需把查询结果集当做入参传入，即可自动完成数据字典键值对转换。
     * <p>
     * 此工具类重载了四个keyValueHandle()方法，
     * 分别用于处理Map封装结果集的分页查询、详情查询
     * 以及相应实体对象封装结果集的分页查询、详情查询
     *
     * @param page
     * @throws Exception
     */
    public static void keyValueHandle(IPage page) throws Exception {
        if (page == null) {
            return;
        }
        List records = page.getRecords();
        if (records == null || records.size() == 0) {
            return;
        }
        List<Map<String, Object>> beanMapList = new LinkedList<>();
        for (Object obj : records) {
            beanMapList.add(keyValueHandle(obj));
        }
        page.setRecords(beanMapList);
    }

    public static List<Map<String, Object>> keyValueHandleList(List list) throws Exception {
        if (CollectionUtil.isEmpty(list)) {
            return null;
        }
        List<Map<String, Object>> beanMapList = new LinkedList<>();
        for (Object obj : list) {
            beanMapList.add(keyValueHandle(obj));
        }
        return beanMapList;
    }

    public static String getDictName(String typeCode, String dicValue) {
        if (StringUtils.isBlank(typeCode)) {
            return null;
        }
        Map<String, String> map = getKeyValueMapByCode(typeCode);
        return map.get(dicValue);
    }

    /***
     * 深度拷贝当前对象的数据
     *
     * @param target
     * @param clazz
     * @param <T>
     * @return
     * @throws Exception
     */
    private static <T> T newProxyInstance(T target, Class clazz) throws Exception {
        Class targetClazz = target.getClass();
        /*由于对象中并不是所有字段都需要被显示，所以不再直接继承原对象beanGenerator.setSuperclass(targetClazz)*/
        BeanGenerator beanGenerator = beanGeneraMap.get(targetClazz).get(clazz);
        //创建代理对象
        T proxyInstance = (T) beanGenerator.create();
        //深度克隆
        BeanUtil.copyProperties(target, proxyInstance);
        //为新增字段设置值
        BeanMap beanMap = BeanMap.create(proxyInstance);
        List<FieldDictionary> fieldList = dictionaryMap.get(targetClazz);
        for (FieldDictionary fieldDic : fieldList) {
            Method method = ReflectionUtils.findMethod(targetClazz, "get" + fieldDic.getFieldName());
            String fieldValue = null;
            try {
                fieldValue = (String) method.invoke(target);
            } catch (Exception e) {
                throw new Exception("数据字典转换异常！");
            }
            String dictName = getDictName(fieldDic.getDicValue(), fieldValue);
            beanMap.put(fieldDic.getFieldNameDic(), dictName);
        }
        return proxyInstance;
    }

    public static <T> void copyProperties(T source, T target) {
        String beanKey = source.toString() + target.toString();
        BeanCopier copier = null;
        if (!beanCopierMap.containsKey(beanKey)) {
            copier = BeanCopier.create(source.getClass(), target.getClass(), false);
            beanCopierMap.put(beanKey, copier);
        } else {
            copier = beanCopierMap.get(beanKey);
        }
        copier.copy(source, target, null);
    }

    /***
     * 数据字段批量转换新方法 可传入泛型 返回对应的实体类 方便导出
     * @param list
     * @param <T>
     * @return
     */
    public static <T> List<T> keyValueHandleListT(List<T> list) throws Exception {
        if (CollectionUtil.isEmpty(list)) {
            return null;
        }
        //循环转换
        List<T> copyList = new ArrayList<>(list.size());
        //找出需要显示的所有字段
        T firstObj = list.get(0);
        Class firstClazz = firstObj.getClass();
        loadBeanGenerator(firstClazz);
        if (dictionaryMap.get(firstClazz).size() == 0) {
            return list;
        }
        for (T item : list) {
            copyList.add(newProxyInstance(item, null));
        }
        return copyList;
    }

    /***
     * 可传入泛型 返回对应的实体类 方便导出
     *
     * @param obj
     * @param <T>
     * @return
     * @throws Exception
     */
    public static <T> T keyValueHandleT(T obj) throws Exception {
        Class firstClazz = obj.getClass();
        loadBeanGenerator(firstClazz);
        return newProxyInstance(obj, null);
    }

    /**
     * 加载BeanGenerator
     *
     * @param clazz
     * @param <T>
     */
    private static <T> void loadBeanGenerator(Class clazz) {
        Map<Class, BeanGenerator> map = beanGeneraMap.get(clazz);
        map = (null == map ? new HashMap<>() : map);
        if (null == map.get(clazz)) {
            BeanGenerator beanGenerator = new BeanGenerator();
            //字段数组
            Field[] fields = clazz.getDeclaredFields();
            List<FieldDictionary> fieldList = new ArrayList<>();
            beanGenerator.setSuperclass(clazz);
            //找出有DTOConverter注解的字段
            for (int i = 0; i < fields.length; i++) {
                //找到有自定义注解的属性
                getDicField(beanGenerator, fields[i], fieldList);
            }
            map.put(null, beanGenerator);
            beanGeneraMap.put(clazz, map);
            dictionaryMap.put(clazz, fieldList);
        }
    }

    private static void getDicField(BeanGenerator beanGenerator, Field f, List<FieldDictionary> fieldList) {
        String fieldName = f.getName();
        //找到有DTOConverter注解的属性
        DictionaryCode dic = f.getAnnotation(DictionaryCode.class);
        if (null != dic) {
            String dicValue = dic.value();
            if (StringUtils.isNotBlank(dicValue)) {
                String fieldNameDic = fieldName + "Dic";
                FieldDictionary fieldDic = new FieldDictionary();
                //添加属性
                beanGenerator.addProperty(fieldNameDic, String.class);
                fieldDic.setFieldName(fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1));
                fieldDic.setFieldNameDic(fieldNameDic);
                fieldDic.setDicValue(dicValue);
                fieldList.add(fieldDic);
            }
        }
    }

    /***
     * 以hashmap的方式 添加数据字典转换字段
     * @param obj
     * @return
     * @throws Exception
     */
    public static Map<String, Object> keyValueHandle(Object obj) throws Exception {
        if (obj == null) {
            return null;
        }
        Map<String, Object> beanMap = new HashMap<>(16);
        Field[] fields = obj.getClass().getDeclaredFields();
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                String name = field.getName();
                // 排除序列化属性
                if ("serialVersionUID".equals(name)) {
                    continue;
                }
                Object value = field.get(obj);
                beanMap.put(name, value);
                if (field.isAnnotationPresent(DictionaryCode.class) && value != null) {
                    DictionaryCode dictionaryCode = field.getAnnotation(DictionaryCode.class);
                    String dicType = dictionaryCode.value();
                    Map<String, String> keyValueMap = getKeyValueMapByCode(dicType);
                    beanMap.put(name + "Dic", keyValueMap.get(value.toString()));
                }
            }
        } catch (Exception e) {
            throw new Exception("数据字典转换异常", e);
        }
        return beanMap;
    }

    /**
     * 根据数据字典类型码获取对应的数据字典值集合
     *
     * @param typeCode
     * @return
     */
    public static Map<String, String> getKeyValueMapByCode(String typeCode) {
        //利用缓存数据，避免重复查库
        Map<String, String> keyValueMap = code2KeyValueMap.get(typeCode);
        if (keyValueMap == null) {
            List<Map<String, String>> keyValueList = new ArrayList<>();
            //List<Map<String, String>> keyValueList = util.dictionaryMapper.getKeyValueListByCode(typeCode);
            if (keyValueList.size() > 0) {
                keyValueMap = new HashMap<>(8);
                for (Map<String, String> dic : keyValueList) {
                    keyValueMap.put(dic.get("value"), dic.get("name"));
                }
                //新数据加入缓存
                code2KeyValueMap.put(typeCode, keyValueMap);
            }
        }
        return keyValueMap;
    }

    public static List<Map<String, String>> getKeyValueListByCode(String typeCode) {
        Map<String, String> keyValueMap = getKeyValueMapByCode(typeCode);
        if (keyValueMap != null) {
            List<Map<String, String>> KeyValueList = new ArrayList<>();
            for (Map.Entry<String, String> entry : keyValueMap.entrySet()) {
                Map<String, String> dic = new HashMap<>(2);
                dic.put("value", entry.getKey());
                dic.put("name", entry.getValue());

                KeyValueList.add(dic);
            }
            return KeyValueList;
        }
        return null;
    }

    /**
     * 添加数据字典到缓存中
     *
     * @param typeCode
     * @return
     */
    public static void putKeyValueMap(String typeCode, Map<String, String> keyValueMap) {
        code2KeyValueMap.put(typeCode, keyValueMap);
    }

    /**
     * 对应数据字典增、删、改的方法中应该调用此方法,
     * 传入对应数据字典类型 typeCode
     * 清除对应 typeCode 的缓存数据
     *
     * @param typeCode
     */
    public static void cleanDictionaryCache(String typeCode) {
        if ("0".equals(typeCode)) {
            code2KeyValueMap.clear();
        } else {
            code2KeyValueMap.remove(typeCode);
        }
    }

    /***
     * 启动初始化
     */
    @PostConstruct
    public void init() {
        util = this;
//        util.dictionaryMapper = this.dictionaryMapper;
    }

}
